﻿//------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Robert MacLean">
//      Copyright © Robert MacLean.  All Rights Reserved.
// </copyright>
//
//  Details on this see: http://rule18.codeplex.com
//  This code released under the terms of the 
//  Microsoft Reciprocal License (MS-RL, http://opensource.org/licenses/ms-rl.html.)
//------------------------------------------------------------------------------

namespace Rule18
{
    using System;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using Microsoft.Win32;
    using Microsoft.WindowsAPICodePack.Dialogs;
    using Microsoft.WindowsAPICodePack.Taskbar;
    using Rule18.Code;
    using Rule18.Core;
    using System.Diagnostics.Contracts;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// Defines the time to show the hint display
        /// </summary>
        private const int hintDisplayTime = 5000;

        /// <summary>
        /// The in memory store of the clips.
        /// </summary>
        private ClipsCollection clips = ClipsCollection.GetClips;

        /// <summary>
        /// The Win7 jumplist
        /// </summary>
        //todo: move to the taskbar class.
        private JumpList jumpList;

        /// <summary>
        /// Defines the path to cmd.exe
        /// </summary>
        private string commandLine = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), Properties.StringResources.CmdExe);

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindow"/> class.
        /// </summary>
        public MainWindow()
        {
            Contract.Ensures(!string.IsNullOrWhiteSpace(versionTextBlock.Text));

            InitializeComponent();
            versionTextBlock.Text = string.Format(CultureInfo.CurrentCulture, Properties.StringResources.VersionInfo, Assembly.GetExecutingAssembly().GetName().Version);
        }

        /// <summary>
        /// Handles the CollectionChanged event of the Values control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        //todo: should find a way to enable this for Win7 only
        private void Values_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Contract.Requires(e != null);

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    {
                        jumpList.ClearAllUserTasks();
                        foreach (Clip clip in clips.Take(10))
                        {
                            jumpList.AddUserTasks(new JumpListLink(commandLine, clip.WideDisplayText)
                            {
                                Arguments = String.Format(CultureInfo.CurrentCulture, Properties.StringResources.ClipCommand, clip.Text)
                            });
                        }

                        jumpList.Refresh();
                        break;
                    }
            }
        }

        /// <summary>
        /// Handles the ClipActivated event of the clips control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The Rule18.Code.FlexiEventArgs&lt;System.String&gt; instance containing the event data.</param>
        private void clips_ClipActivated(object sender, FlexiEventArgs<string> e)
        {
            Contract.Requires(e != null);

            Clipboard.SetText(e.Data);
            Taskbar.FlashTaskbarGreen();
        }

        /// <summary>
        /// Handles the Click event of the addClipButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void addClipButton_Click(object sender, RoutedEventArgs e)
        {
            if (Clipboard.ContainsText())
            {
                clips.AddClip(Clipboard.GetText());
            }
            else
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(ShowHint), Properties.StringResources.NoTextMessage);
            }
        }

        /// <summary>
        /// Shows the hint.
        /// </summary>
        /// <param name="stateInfo">The state info.</param>
        private void ShowHint(object stateInfo)
        {
            Contract.Requires(stateInfo != null);
            Contract.Requires(stateInfo is string);
            Contract.Requires(hintDisplayTime > 0);

            hintTextBlock.Dispatcher.Invoke(new Action(delegate()
            {
                hintTextBlock.Text = string.Format(CultureInfo.CurrentCulture, Properties.StringResources.Hint, stateInfo);
            }));


            Thread.Sleep(hintDisplayTime);

            hintTextBlock.Dispatcher.Invoke(new Action(delegate()
            {
                hintTextBlock.Text = string.Empty;
            }));
        }

        /// <summary>
        /// Handles the Click event of the saveSetButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void saveSetButton_Click(object sender, RoutedEventArgs e)
        {
            if (clips.FileName == null)
            {
                SaveFileDialog saveDialog = new SaveFileDialog();
                saveDialog.AddExtension = true;
                saveDialog.CheckFileExists = false;
                saveDialog.CheckPathExists = true;
                saveDialog.CreatePrompt = false;
                saveDialog.DefaultExt = Properties.StringResources.R18FileExtension;
                saveDialog.Filter = Properties.StringResources.Rule18DialogFilter;
                saveDialog.OverwritePrompt = true;
                saveDialog.Title = Properties.StringResources.SaveDialogTitle;
                saveDialog.ValidateNames = true;

                if (saveDialog.ShowDialog().GetValueOrDefault(false))
                {
                    clips.FileName = new System.IO.FileInfo(saveDialog.FileName);
                }
            }
            else
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(ShowHint), Properties.StringResources.SaveHint);
            }

            clips.Save();
        }

        /// <summary>
        /// Opens the file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        internal void OpenFile(string fileName)
        {
            Contract.Requires(!string.IsNullOrWhiteSpace(fileName));           

            clips.Load(fileName);
            jumpList.AddToRecent(fileName);
        }

        /// <summary>
        /// Handles the Click event of the loadSetButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void loadSetButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog();
            openDialog.AddExtension = true;
            openDialog.CheckFileExists = true;
            openDialog.CheckPathExists = true;
            openDialog.DefaultExt = Properties.StringResources.R18FileExtension;
            openDialog.Filter = Properties.StringResources.Rule18DialogFilter;
            openDialog.Title = Properties.StringResources.OpenDialogTitle;
            openDialog.ValidateNames = true;

            if (openDialog.ShowDialog().GetValueOrDefault(false))
            {
                OpenFile(openDialog.FileName);
            }
        }

        /// <summary>
        /// Handles the Closing event of the Window control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Contract.Requires(jumpList != null);            

            jumpList.ClearAllUserTasks();

            if (clips.FileName != null)
            {
                clips.Save();
            }
            else
            {
                if (clips.Count > 0)
                {
                    using (TaskDialog taskDialog = new TaskDialog())
                    {
                        taskDialog.Cancelable = false;
                        taskDialog.Caption = Properties.StringResources.CloseConfirmationTitle;
                        taskDialog.StandardButtons = TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No | TaskDialogStandardButtons.Cancel;

                        taskDialog.Text = Properties.StringResources.CloseConfirmationText;
                        taskDialog.StartupLocation = TaskDialogStartupLocation.CenterOwner;
                        switch (taskDialog.Show())
                        {
                            case TaskDialogResult.Yes:
                                {
                                    saveSetButton_Click(this, null);
                                    break;
                                }
                            case TaskDialogResult.Cancel:
                                {
                                    e.Cancel = true;
                                    return;
                                }
                        }
                    }
                }
            }

            if (clips != null)
            {
                clips.Dispose();
            }
        }

        /// <summary>
        /// Handles the Loaded event of the Window control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Contract.Ensures(jumpList != null);

            jumpList = JumpList.CreateJumpList();
            jumpList.KnownCategoryToDisplay = JumpListKnownCategoryType.Frequent | JumpListKnownCategoryType.Recent;
            jumpList.Refresh();

            clips.ClipActivated += new EventHandler<FlexiEventArgs<string>>(clips_ClipActivated);
            clipsListBox.ItemsSource = clips;
            clips.CollectionChanged += new NotifyCollectionChangedEventHandler(Values_CollectionChanged);

            if (Environment.GetCommandLineArgs().Length > 1)
            {
                OpenFile(Environment.GetCommandLineArgs()[1]);
            }
        }

        /// <summary>
        /// Handles the Click event of the DeleteMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteMenuItem_Click(object sender, RoutedEventArgs e)
        {
            Contract.Requires(e != null);

            clips.Delete((Guid)((MenuItem)e.Source).CommandParameter);
        }

        /// <summary>
        /// Handles the MouseLeftButtonUp event of the linkTextBlock control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void linkTextBlock_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            System.Diagnostics.Process.Start(Properties.StringResources.Rule18URL);
        }

        /// <summary>
        /// Handles the KeyUp event of the clipsListBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        private void clipsListBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            Contract.Requires(e != null);

            if (e.Key == System.Windows.Input.Key.Delete)
            {
                if (clipsListBox.SelectedItem != null)
                {
                    clips.Delete(((Clip)clipsListBox.SelectedItem).Id);
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the HotkeyChangeButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void HotkeyChangeButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {           
            HotkeyChangeWindow hotkeyChangeWindow = new HotkeyChangeWindow();
            bool? result = hotkeyChangeWindow.ShowDialog();
            if (result.HasValue && result.Value)
            {
                clips.UpdateKey(((Guid)((Button)e.Source).CommandParameter), hotkeyChangeWindow.Key);
            }
        }

        /// <summary>
        /// Handles the GotFocus event of the textBlock control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void textBlock_GotFocus(object sender, RoutedEventArgs e)
        {
            TextBox textbox = (TextBox)e.Source;
            textbox.Text = clips.Where(c => c.Id == (Guid)textbox.Tag).Single().Text;
            textbox.IsReadOnly = false;
        }

        /// <summary>
        /// Handles the LostFocus event of the textBlock control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void textBlock_LostFocus(object sender, RoutedEventArgs e)
        {
            TextBox textbox = (TextBox)e.Source;
            textbox.IsReadOnly = true;
            Guid newId = clips.UpdateClipText((Guid)textbox.Tag, textbox.Text);
            textbox.Text = clips.Where(c => c.Id == newId).Single().WideDisplayText;
        }

        /// <summary>
        /// Handles the Click event of the DeleteButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(ShowHint), Properties.StringResources.DeleteHint);
            clips.Delete((Guid)((Button)e.Source).CommandParameter);
        }

        /// <summary>
        /// Handles the ItemsReordered event of the clipsListBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void clipsListBox_ItemsReordered(object sender, RoutedEventArgs e)
        {
            clips.Move(clipsListBox.OriginalItemIndex, clipsListBox.SelectedIndex);
        }

        /// <summary>
        /// Handles the Click event of the newSetButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void newSetButton_Click(object sender, RoutedEventArgs e)
        {
            Contract.Requires(this.clips != null);

            clips.ClearClips();
        }
    }
}